<!--
 * 默认为单个上传截图（multiple:false），配置auto-upload: false，阻止选中文件后自动上传，由cropper组件截图后再上传返回文件；
 * 当组件为多选上传时（multiple:true），强制auto-upload: true，上传时不打开裁剪组件，上传完成后点击上传组件的裁剪按钮进行裁剪操作；
-->
<script>
import i18n from '@src/locales';
import { VueCropper } from 'vue-cropper';
import Uploader from 'packages/BaseUpload/uploader';
import { deleteServerFiles } from 'pub-bbx-utils';

function defaultFunc() {}

export default {
  name: 'biz-cropper-upload',
  props: {
    action: {
      type: String,
      default: 'unused',
    },
    headers: {
      type: Object,
      default() {
        return {};
      },
    },
    data: Object,
    name: {
      type: String,
      default: 'file',
    },
    withCredentials: {
      type: Boolean,
      default: false,
    },
    showFileList: {
      type: Boolean,
      default: true,
    },
    accept: {
      type: String,
      default: 'image/*',
    },
    beforeUpload: Function,
    beforeRemove: Function,
    onRemove: {
      type: Function,
      default: defaultFunc,
    },
    onChange: {
      type: Function,
      default: defaultFunc,
    },
    onPreview: {
      type: Function,
      default: defaultFunc,
    },
    onSuccess: {
      type: Function,
      default: defaultFunc,
    },
    onProgress: {
      type: Function,
      default: defaultFunc,
    },
    onError: {
      type: Function,
      default: defaultFunc,
    },
    fileList: {
      type: Array,
      default() {
        return [];
      },
    },
    autoUpload: {
      type: Boolean,
      default: false,
    },
    listType: {
      type: String,
      default: 'picture-card', // text,picture,picture-card
    },
    httpRequest: Function,
    disabled: Boolean,
    limit: Number,
    limitSize: Number,
    multiple: {
      type: Boolean,
      default: false,
    },
    onExceed: {
      type: Function,
      default: defaultFunc,
    },
    tips: {
      type: String,
      default: '',
    },
    cropper: {
      type: Object,
      default: () => {},
    },
    // 表单操作模式
    formEditingMode: {
      type: String,
      default: '', // create | edit
    },
    getDeleteFiles: {
      type: Function,
      default: defaultFunc,
    },
    // 指定裁剪后的像素大小
    cropSize: {
      type: Object,
      default: () => {
        return {}
      }
    },
    // 是否显示原图上传开关
    showOriginSwitch: {
      type: Boolean,
      default: true
    }
  },
  data() {
    return {
      dialogVisible: false,
      loading: false,
      canCropper: 1,
      currentFile: {},
      beforeUploadFile: {},
      fileListCopy: this.fileList,
      cropperOpts: Object.assign(
        {},
        {
          img: '', // 裁剪图片的地址
          info: false, // 裁剪框的大小信息
          outputSize: 1, // 裁剪生成图片的质量
          outputType: 'jpeg', // 裁剪生成图片的格式
          canScale: true, // 图片是否允许滚轮缩放
          autoCrop: true, // 是否默认生成截图框
          autoCropWidth: 300, // 默认生成截图框宽度
          autoCropHeight: 300, // 默认生成截图框高度
          fixedBox: false, // 固定截图框大小 不允许改变
          fixed: true, // 是否开启截图框宽高固定比例
          fixedNumber: [1, 1], // 截图框的宽高比例
          full: true, // 是否输出原图比例的截图
          canMoveBox: true, // 截图框能否拖动
          original: false, // 上传图片按照原始比例渲染
          centerBox: true, // 截图框是否被限制在图片里面
          infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
        },
        this.cropper
      ),
      originOpts: Object.assign(
        {},
        {
          img: '', // 裁剪图片的地址
          info: false, // 裁剪框的大小信息
          outputSize: 1, // 裁剪生成图片的质量
          outputType: 'jpeg', // 裁剪生成图片的格式
          fixed:true,
          full: true, // 是否输出原图比例的截图
          canMoveBox: false, // 截图框能否拖动
          original: false, // 上传图片按照原始比例渲染
          centerBox: true, // 截图框是否被限制在图片里面
          infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
          canScale:false,
          autoCropWidth: 'max', // 用来原图上传截取全图
          autoCropHeight: 'max'
        },
        // this.cropper   // 原图上传不接受其他参数
      ),
      originSwitch:false   // 原图上传开关
    };
  },
  methods: {
    // 覆盖默认的上传行为，可以自定义上传的实现
    handleHttpRequest(options) {
      return this.httpRequest(options);
    },

    // 文件列表移除文件时的钩子
    handleRemove(file, fileList) {
      this.currentFile = {};
      this.fileListCopy = fileList;
      this.onRemove(file, fileList);
      if (this.formEditingMode == 'create') {
        deleteServerFiles([file.fileId])
      } else if (this.formEditingMode == 'edit') {
        file.fileId && this.$emit('getDeleteFiles', [file.fileId]);
      }
    },

    // 点击文件列表中已上传的文件时的钩子
    handlePreview(file) {
      this.onPreview(file);
    },

    handleBeforeUpload(file) {
      // autoUpload = true才会走这里
      // 此处暂存file 方便在multiple模式 单个上传的情况下，获取当前的file，为什么不用currentFile，看下面
      this.beforeUploadFile = file;
      // return false，会走到handleRemove 清除 currentFile
      if(this.canCropper) return false;
      return this.beforeUpload(file);
    },

    // 文件状态改变时的钩子，添加文件、上传成功和上传失败时都会被调用
    handleChange(file, fileList) {
      this.currentFile = file;
      this.fileListCopy = fileList;
      
      // 单选 或者 多选只传单个可以裁剪图片
      const UploadDomFiles = this.$refs.uploader?.$refs['upload-inner']?.$refs['input']?.files || '';
      // 这里判断通过原生input的files长度，既打开文件夹选中的文件个数，来判断是否需要裁剪
      this.canCropper = !this.multiple ? 1 : UploadDomFiles && UploadDomFiles.length === 1 ? 1 : 0;
      
      if(this.canCropper) {
        // 单个上传
        const files = file.raw;
        let reader = new FileReader(); // 创建文件读取对象
        reader.onload = async e => {
          // 检查原始文件的MIME类型，并设置outputType
          const mimeType = files.type;
          let outputType;
          if (mimeType === 'image/jpeg') {
            outputType = 'jpeg';
          } else if (mimeType === 'image/png') {
            outputType = 'png';
          } else if (mimeType === 'image/jpg') {
            outputType = 'jpg';
          } else {
            outputType = 'jpeg';
          }

          // 更新裁剪配置
          this.cropperOpts.outputType = outputType;
          this.originOpts.outputType = outputType;
          let data;
          if (typeof e.target.result === 'object') {
            let base64 = this.arrayBufferToBase64(e.target.result);
            data = `data:image/png;base64,${base64}`;
            // 把Array Buffer转化为blob 如果是base64不需要
            // data = window.URL.createObjectURL(new Blob([e.target.result]));
          } else {
            data = e.target.result;
          }
          this.cropperOpts.img = data; // 设置option的初始image
          this.originOpts.img = data; // 设置option的初始image
          this.$refs.cropper?.refresh() // 同张图片取消后再选择刷新
          this.dialogVisible = true;
        };
        reader.readAsArrayBuffer(files);
      }

      this.onChange(file, fileList)
    },
    arrayBufferToBase64(buffer) {
      //第一步，将ArrayBuffer转为二进制字符串
      let binary = '';
      let bytes = new Uint8Array(buffer);
      for (let len = bytes.byteLength, i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
      }
      //将二进制字符串转为base64字符串
      return window.btoa(binary);
    },
    // 文件上传成功时的钩子
    handleSuccess(res, file, fileList) {
      this.currentFile = file;
      this.fileListCopy = fileList;
      this.onSuccess(res, file, fileList);
    },

    // 文件上传失败时的钩子
    handleError(res, file, fileList) {
      this.currentFile = file;
      this.fileListCopy = fileList;
      this.onError(res, file, fileList);
    },

    // 取消裁剪
    cancelCropper() {
      this.dialogVisible = false;
      if(!this.multiple) {
        this.fileListCopy.pop();
        this.$refs.uploader.uploadFiles.pop();
      }
      this.$refs?.cropper.clearCrop();
    },

    // 确认裁剪
    confirmCropper() {
      this.$refs.cropper.getCropBlob(blob => {
        let processBlob = blob; // 默认情况下，使用原始blob
        const isCropSizeEmpty = Object.keys(this.cropSize || {}).length === 0;

        const createFileAndUpload = (processedBlob) => {
          const file = new File(
            [processedBlob],
            this.beforeUploadFile.name || `${Date.now()}.${this.cropperOpts.outputType}`,
            { type: this.cropperOpts.outputType || 'image/png' }
          );
          this.loading = true;
          Uploader.upload(file, '/files/upload')
            .then(result => {
              if (result.succ) {
                this.replaceUploadCache(processedBlob);
                this.$emit('cropperDone', this.currentFile, result.data);
                this.dialogVisible = false;
              }
            })
            .catch(err => {
              console.warn(err);
            })
            .finally(() => {
              this.loading = false;
            });
        };

        if (!isCropSizeEmpty) {
          // 如果 this.cropSize 不为空，调整blob大小
          let canvas = document.createElement('canvas');
          let ctx = canvas.getContext('2d');
          canvas.width = this.cropSize.width;
          canvas.height = this.cropSize.height;

          let img = new Image();
          img.onload = () => {
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            canvas.toBlob((newBlob) => {
              processBlob = newBlob; // 更新处理后的blob
              createFileAndUpload(processBlob); // 使用处理后的blob上传
            }, this.cropperOpts.outputType || 'image/png');
          };
          img.src = URL.createObjectURL(blob);
        } else {
          // 如果 this.cropSize 为空，直接上传原始blob
          createFileAndUpload(processBlob);
        }
      });
    },

    // 替换掉el-upload组件中的图片缓存
    replaceUploadCache(blob) {
      const file = {
        status: 'success',
        name: this.currentFile.name,
        size: blob.size,
        percentage: 0,
        uid: Date.now(),
        raw: blob,
      };
      try {
        file.url = window.URL.createObjectURL(blob);
      } catch (err) {
        console.error(err);
        return;
      }
      this.currentFile = file;
      // 这里意思其实是 如果是autoUpload=false要清一下upload的缓存
      if(!this.multiple) { 
        this.$refs.uploader.uploadFiles.pop();
      }
      this.$refs.uploader.uploadFiles.push(file);
    },
    changeMode(value){
      this.originSwitch = value
      this.$refs?.cropper?.refresh()
    }
  },
  components: { VueCropper },
  computed: {
    cropperData(){
      return {
        props: this.originSwitch ? this.originOpts : this.cropperOpts,
        ref: 'cropper',
        class: {},
      }
    }
  },
  render(h) {
    // 上传按钮的属性
    // https://element.eleme.cn/#/zh-CN/component/upload
    // 多选的强制自动上传
    const autoUpload = this.multiple ? 1 : 0;
    const uploadData = {
      props: {
        action: this.action,
        headers: this.headers,
        name: this.name,
        data: this.data,
        accept: this.accept,
        disabled: this.disabled,
        limit: this.limit,
        multiple: this.multiple,
        'with-credentials': this.withCredentials,
        'file-list': this.fileList,
        'auto-upload': Boolean(autoUpload),
        'list-type': this.listType,
        'on-exceed': this.onExceed,
        'on-progress': this.onProgress,
        'before-upload': this.handleBeforeUpload,
        'on-success': this.handleSuccess,
        'on-error': this.handleError,
        'on-preview': this.handlePreview,
        'on-remove': this.handleRemove,
        'on-change': this.handleChange,
        'http-request': this.handleHttpRequest,
      },
      ref: 'uploader',
      class: {},
    };

    return (
      <div class="cropper-upload">
        <el-upload {...uploadData}>
          <i class="el-icon-plus" />
          <div slot="tip" class="el-upload__tip">
            <span class="font-12 color-999">
              {this.tips}
            </span>
          </div>
        </el-upload>

        <el-dialog title={i18n.t('common.base.imageClip')} append-to-body={true} visible={this.dialogVisible} { ...{ on: { 'update:visible': val => { this.dialogVisible = val } }}}>
          <div class="cropper-content">
            <div class="cropper" style="text-align:center">
              <vueCropper {...this.cropperData}/>
            </div>
            <div class="mode-info">
              {this.showOriginSwitch && <el-checkbox onChange={this.changeMode}>{this.$t('common.bizComponents.bizCropperUpload.des1')}</el-checkbox>}
              <div class='mode-msg' v-show={!this.originSwitch}>{this.$t('common.bizComponents.bizCropperUpload.des2')}</div>
            </div>
          </div>
          <div slot="footer" class="dialog-footer">
            <el-button onClick={() => this.cancelCropper()}>{this.$t('common.base.cancel')}</el-button>
            <el-button
              type="primary"
              onClick={() => this.confirmCropper()}
              loading={this.loading}
            >
              {this.$t('common.base.confirm')}
            </el-button>
          </div>
        </el-dialog>
      </div>
    );
  },
};
</script>
<style lang="scss" scoped>
.cropper {
  width: auto;
  height: 300px;
}
.mode-info{
  padding-top: 12px;
  display:flex;
  justify-content: flex-start;
  .mode-msg{
    margin-left: auto;
    color:#8C8C8C;
  }
  .el-checkbox{
    display: block;
    width: auto;
  }
}
</style>
